package com.yj.spring.charpter04;

import cn.hutool.core.util.StrUtil;
import com.yj.spring.common.ArgumentValue;
import com.yj.spring.common.PropertyValue;
import lombok.SneakyThrows;

import java.lang.reflect.Method;
import java.util.List;

public class ClassPathXmlApplicationContext {

    SimpleBeanFactory beanFactory;

    String[] configLocations;

    public ClassPathXmlApplicationContext(String configLocation) {
        configLocations = new String[]{configLocation};
        beanFactory = new SimpleBeanFactory();
        XmlResource resource = new XmlResource(configLocation);
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        reader.loadBeanDefinitions(resource);
    }


    public void refresh() throws Exception {
        //doRefresh();
        for(String beanName : beanFactory.beanDefinitionNames){
            getBean(beanName);
        }
    }

    @SneakyThrows
    private void doRefresh() {

        for (BeanDefinition beanDefinition : beanFactory.beanDefinitions) {
            Object bean = null;
            String id = beanDefinition.getId();
            String className = beanDefinition.getClassName();
            List<ArgumentValue> argumentValues = beanDefinition.getArgumentValues();
            List<PropertyValue> propertyValues = beanDefinition.getPropertyValues();

            Object[] parameters = new Object[argumentValues.size()];
            Class[] parameterTypes = new Class[argumentValues.size()];
            for (int i = 0; i < argumentValues.size(); i++) {
                ArgumentValue argumentValue = argumentValues.get(i);
                String type = argumentValue.getType();
                if ("int".equals(type) || "java.lang.Integer".equals(type)) {
                    parameters[i] = Integer.parseInt((String) argumentValue.getValue());
                    parameterTypes[i] = Integer.class;
                } else if ("String".equals(type) || "java.lang.String".equals(type)) {
                    parameters[i] = (String) argumentValue.getValue();
                    parameterTypes[i] = String.class;
                } else if ("boolean".equals(type) || "java.lang.Boolean".equals(type)) {
                    parameters[i] = Boolean.parseBoolean((String) argumentValue.getValue());
                    parameterTypes[i] = Boolean.class;
                } else {
                    parameters[i] = argumentValue.getValue();
                    parameterTypes[i] = String.class;
                }
            }
            Class<?> aClass = Class.forName(className);
            bean = aClass.getDeclaredConstructor(parameterTypes).newInstance(parameters);

            Class[] propertyType = new Class[1];
            Object[] propertyParameter = new Object[1];
            for (int i = 0; i < propertyValues.size(); i++) {
                PropertyValue propertyValue = propertyValues.get(i);
                String type = propertyValue.getType();
                String ref = propertyValues.get(i).getRef();
                if (StrUtil.isNotEmpty(ref)){
                    propertyType[0] = Class.forName(type);
                    bean = beanFactory.getBean(ref);
                    propertyParameter[0] = bean;
                }else{

                    if ("int".equals(type) || "java.lang.Integer".equals(type)) {
                        propertyType[0] = Integer.class;
                    }else if ("string".equals(type) || "java.lang.String".equals(type)){
                        propertyType[0] = String.class;
                    }else if ("boolean".equals(type) || "java.lang.Boolean".equals(type)){
                        propertyType[0] = Boolean.class;
                    }else {
                        propertyType[0] = String.class;
                    }
                    propertyParameter[0] = propertyValue.getValue();

                }
                String methodName = "set" + propertyValue.getName().substring(0, 1).toUpperCase() + propertyValue.getName().substring(1);
                Method method = aClass.getMethod(methodName, propertyType);
                method.invoke(bean, propertyParameter);

            }
            beanFactory.registerBean(id, bean);
        }
    }

    public Object getBean(String beanName){
       return this.beanFactory.getBean(beanName);
    }

}
